package com.xhj.mybatis.type;

import com.xhj.mybatis.io.Resources;
import com.xhj.mybatis.session.Configuration;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
 * UnknownTypeHandler
 *
 * @author XJks
 * @description TODO:TypeHandlerRegistry 中兜底的类型处理器。
 */
public class UnknownTypeHandler extends BaseTypeHandler<Object> {

    /**
     * 定义一个静态常量，ObjectTypeHandler 用于处理未知类型
     */
    private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();

    /**
     * 保存 MyBatis 的配置信息
     */
    private final Configuration config;

    /**
     * 用于延迟获取 TypeHandlerRegistry（类型处理器注册表）的 Supplier
     */
    private final Supplier<TypeHandlerRegistry> typeHandlerRegistrySupplier;

    /**
     * 构造方法，传入 MyBatis 的 Configuration 配置对象
     *
     * @param configuration MyBatis 配置对象
     */
    public UnknownTypeHandler(Configuration configuration) {
        // 保存配置对象
        this.config = configuration;
        // 通过 lambda 表达式延迟获取 TypeHandlerRegistry
        this.typeHandlerRegistrySupplier = configuration::getTypeHandlerRegistry;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        // 根据参数和 JDBC 类型解析出合适的 TypeHandler
        TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
        // 使用解析出来的 handler 设置参数
        handler.setParameter(ps, i, parameter, jdbcType);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 根据 ResultSet 和列名解析出合适的 TypeHandler
        TypeHandler<?> handler = resolveTypeHandler(rs, columnName);
        // 使用 handler 获取结果
        return handler.getResult(rs, columnName);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 根据 ResultSetMetaData 和列索引解析出合适的 TypeHandler
        TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);
        // 如果没有找到合适的 handler 或者 handler 还是 UnknownTypeHandler，则用默认的 OBJECT_TYPE_HANDLER
        if (handler == null || handler instanceof UnknownTypeHandler) {
            handler = OBJECT_TYPE_HANDLER;
        }
        // 使用 handler 获取结果
        return handler.getResult(rs, columnIndex);
    }

    /**
     * 根据参数和 JDBC 类型解析出合适的 TypeHandler
     *
     * @param parameter 参数
     * @param jdbcType  JDBC 类型
     * @return 解析出的 TypeHandler
     */
    private TypeHandler<?> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
        TypeHandler<?> handler;
        // 如果参数为 null，直接用默认的 OBJECT_TYPE_HANDLER
        if (parameter == null) {
            handler = OBJECT_TYPE_HANDLER;
        } else {
            // 根据参数类型和 JDBC 类型从注册表获取 handler
            handler = typeHandlerRegistrySupplier.get().getTypeHandler(parameter.getClass(), jdbcType);
            // 如果没有找到或还是 UnknownTypeHandler，则用默认的 OBJECT_TYPE_HANDLER
            if (handler == null || handler instanceof UnknownTypeHandler) {
                handler = OBJECT_TYPE_HANDLER;
            }
        }
        return handler;
    }

    /**
     * 根据 ResultSet 和列名解析出合适的 TypeHandler
     */
    private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
        try {
            // 创建一个列名到索引的映射
            Map<String, Integer> columnIndexLookup;
            columnIndexLookup = new HashMap<>();
            // 获取 ResultSet 的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            // 获取列的数量
            int count = rsmd.getColumnCount();
            // 判断是否使用列标签
            boolean useColumnLabel = config.isUseColumnLabel();
            // 遍历所有列，建立列名（或标签）到索引的映射
            for (int i = 1; i <= count; i++) {
                String name = useColumnLabel ? rsmd.getColumnLabel(i) : rsmd.getColumnName(i);
                columnIndexLookup.put(name, i);
            }
            // 获取指定列名对应的索引
            Integer columnIndex = columnIndexLookup.get(column);
            TypeHandler<?> handler = null;
            // 如果找到了索引，则根据元数据和索引解析 handler
            if (columnIndex != null) {
                handler = resolveTypeHandler(rsmd, columnIndex);
            }
            // 如果没有找到或还是 UnknownTypeHandler，则用默认的 OBJECT_TYPE_HANDLER
            if (handler == null || handler instanceof UnknownTypeHandler) {
                handler = OBJECT_TYPE_HANDLER;
            }
            return handler;
        } catch (SQLException e) {
            // 捕获异常并抛出自定义异常
            throw new RuntimeException("Error determining JDBC type for column " + column + ".  Cause: " + e, e);
        }
    }

    /**
     * 根据 ResultSetMetaData 和列索引解析出合适的 TypeHandler
     */
    private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) {
        TypeHandler<?> handler = null;
        // 安全获取 JDBC 类型
        JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
        // 安全获取 Java 类型
        Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
        // 优先同时有 javaType 和 jdbcType
        if (javaType != null && jdbcType != null) {
            handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType, jdbcType);
        } else if (javaType != null) {
            // 只有 javaType
            handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType);
        } else if (jdbcType != null) {
            // 只有 jdbcType
            handler = typeHandlerRegistrySupplier.get().getTypeHandler(jdbcType);
        }
        return handler;
    }

    /**
     * 安全获取指定列的 JDBC 类型
     */
    private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
        try {
            // 通过列类型代码获取 JdbcType
            return JdbcType.forCode(rsmd.getColumnType(columnIndex));
        } catch (Exception e) {
            // 捕获异常返回 null
            return null;
        }
    }

    /**
     * 安全获取指定列的 Java 类型
     */
    private Class<?> safeGetClassForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
        try {
            // 通过列的类名获取 Class 对象
            return Resources.classForName(rsmd.getColumnClassName(columnIndex));
        } catch (Exception e) {
            // 捕获异常返回 null
            return null;
        }
    }

}
